<?php
namespace juven\libs {
    \defined( '_JEXEC' ) or die( 'Restricted access' );

    /**
     * @author George Zeakis <zeageorge@gmail.com>
     */
    class User {
        
        /** @var DynamicContainer */
        protected $_user_data;
        
        public function __construct(){
            $this->_user_data = new DynamicContainer();
            
            $EVENT_onGuestUserRequest = juven::eventsManager()->subscribe(self::EVENT_onGuestUserRequest())->payload = $this->_eventsHandlerPointer;
            $EVENT_onFirstTimeVisitorRequest = juven::eventsManager()->subscribe(self::EVENT_onFirstTimeVisitorRequest())->payload = function(){
                echo "<pre><br />";
                echo juven::config()->systemAdminEmailAddress."<br />";
                print_r(func_get_args());
                echo "</pre><br />";
            };
            
            \session_start();
            $session_record = $this->get_session_record_from_database(\session_id());
            if (!isset($session_record["session_id"])){ // Session does not exists in database, GUEST user
                $this->_user_data = new DynamicContainer($this->getUserDataFromDatabase(juven::config()->db->guest_user_id));
                $dtp = new \stdClass();
                $dtp->session_id = \session_id();
                $dtp->user_data = $this->_user_data;
                
                juven::eventsManager()->publish(User::EVENT_onGuestUserRequest(), $dtp);
            } else {
                
            }
        }

        /**
         * @param string|integer $user_id 
         * @return array Returns assoc array
         */
        protected function getUserDataFromDatabase($user_id){
            $dtp = new \stdClass();
            $dtp->dbConnectionData = juven::config()->db->connectionData;
            $dtp->tableName = Tools::getPrefixedTableName(juven::config()->db->tables->users);
            $dtp->sqlQuery  = "SELECT * FROM `".$dtp->tableName."` WHERE `user_id`=:uID LIMIT 1";
            $dtp->bindParams= array('uID'=>(string)$user_id);
            $dtp->continue  = \TRUE;
            $dtp->returnResults = array();

            juven::eventsManager()->publish(self::EVENT_onBefore_GetUserDataFromDatabase(), $dtp);
            if ($dtp->continue===\TRUE){
                $dtp->getUserDataFromDatabase_selectResults = $this->_runSelectQuery($dtp->dbConnectionData, $dtp->sqlQuery, $dtp->bindParams);
                if (\count($dtp->getUserDataFromDatabase_selectResults)==1){
                    $dtp->returnResults = $dtp->getUserDataFromDatabase_selectResults[0];
                    if (isset($dtp->returnResults["user_password"])){ unset($dtp->returnResults["user_password"]); }
                    $dtp->returnResults['user_date_last_access'] = \date ("Y-m-d H:i:s", \time());
                    $dtp->returnResults['extra_data']       = $this->getUser_ExtraDataFromDatabase(   $dtp->returnResults["user_id"]);
                    $dtp->returnResults['groups']           = $this->getUser_GroupsFromDatabase(      $dtp->returnResults["user_id"]);
                    
                    $dtp->returnResults['HTTP_Requests']     = $this->getUser_HTTP_RequestsFromDatabase($dtp->returnResults["user_id"]);
//                    $dtp->returnResults['menus']            = $this->getUser_MenusFromDatabase(       $dtp->returnResults["user_id"]);
                }
            }
            juven::eventsManager()->publish(self::EVENT_onAfter_getUserDataFromDatabase(), $dtp);
            return $dtp->returnResults;
        }
        
        /**
         * @param string|integer $userID
         * @return array Returns assoc array
         */
        protected function getUser_HTTP_RequestsFromDatabase($userID){
            $dtp = new \stdClass();
            $dtp->dbConnectionData = juven::config()->db->connectionData;
            $dtp->table_users_groups         = Tools::getPrefixedTableName(juven::config()->db->tables->users_groups);
            $dtp->table_http_requests        = Tools::getPrefixedTableName(juven::config()->db->tables->http_requests);
            $dtp->table_http_requests_groups = Tools::getPrefixedTableName(juven::config()->db->tables->http_requests_groups);
            $dtp->bindParams         = array('uID'=>(string)$userID);
            $dtp->continue           = \TRUE;
            $dtp->user_httpRequests  = array();
            $dtp->sqlQuery = "SELECT DISTINCT `".$dtp->table_http_requests."`.`request_id`,`".
                                                 $dtp->table_http_requests."`.`request_parent_id`,`".
                                                 $dtp->table_http_requests."`.`request_value` FROM `".
                                                 $dtp->table_http_requests."` INNER JOIN (`".$dtp->table_http_requests_groups."` ".
                            "INNER JOIN (SELECT `".$dtp->table_users_groups."`.`relation_group_id` FROM `".$dtp->table_users_groups.
                            "` WHERE `".$dtp->table_users_groups."`.`relation_user_id`=:uID) AS `T` ".
                            "ON `".$dtp->table_http_requests_groups."`.`relation_group_id`=`T`.`relation_group_id`) ".
                            "ON `".$dtp->table_http_requests."`.`request_id`=`".$dtp->table_http_requests_groups.
                            "`.`relation_request_id` WHERE `".$dtp->table_http_requests."`.`request_status`=1";
            juven::eventsManager()->publish(self::EVENT_onBefore_GetUserHttpRequestsFromDatabase(), $dtp);
            if ($dtp->continue===\TRUE){
                $select_results = $this->_runSelectQuery($dtp->dbConnectionData, $dtp->sqlQuery, $dtp->bindParams);
                if (\count($select_results)>'0'){ 
                    $dtp->user_httpRequests = Tools::turnResources2aTree($select_results, 0, "request_id", "request_parent_id");
                }
            }
            juven::eventsManager()->publish(self::EVENT_onAfter_GetUserHttpRequestsFromDatabase(), $dtp);
            return $dtp->user_httpRequests;
        }
        
        /**
         * @param string|integer $userID
         * @return array Returns assoc array
         */
        protected function getUser_GroupsFromDatabase($userID){
            $dtp = new \stdClass();
            $dtp->dbConnectionData   = juven::config()->db->connectionData;
            $dtp->table_groups       = Tools::getPrefixedTableName(juven::config()->db->tables->groups);
            $dtp->table_users_groups = Tools::getPrefixedTableName(juven::config()->db->tables->users_groups);
            $dtp->bindParams         = array('uID'=>(string)$userID);
            $dtp->continue           = \TRUE;
            $dtp->user_groups        = array("flat"=>array(), "tree"=>array());
            
            $dtp->sqlQuery = "SELECT `".$dtp->table_groups."`.group_id,`".$dtp->table_groups."`.group_parent_id FROM `".$dtp->table_groups."` INNER JOIN ".
                             "(SELECT `".$dtp->table_users_groups."`.`relation_group_id` FROM `".$dtp->table_users_groups."` ".
                             "WHERE `".$dtp->table_users_groups."`.`relation_user_id`=:uID) AS `T` ".
                             "ON `".$dtp->table_groups."`.`group_id`=`T`.`relation_group_id` WHERE `".$dtp->table_groups."`.`group_status`=1";
            juven::eventsManager()->publish(self::EVENT_onBefore_GetUserGroupsFromDatabase(), $dtp);
            if ($dtp->continue===\TRUE){
                $select_results = $this->_runSelectQuery($dtp->dbConnectionData, $dtp->sqlQuery, $dtp->bindParams);
                if (\count($select_results)>'0'){ 
                    $dtp->user_groups["flat"] = $select_results; 
                    $dtp->user_groups["tree"] = Tools::turnResources2aTree($select_results, 0, "group_id", "group_parent_id", "c");
                }
            }
            juven::eventsManager()->publish(self::EVENT_onAfter_GetUserGroupsFromDatabase(), $dtp);
            return $dtp->user_groups;
        }
        
        /**
         * @param string|integer $userID
         * @return array Returns assoc array
         */
        protected function getUser_ExtraDataFromDatabase($user_id){
            $dtp                   = new \stdClass();
            $dtp->dbConnectionData = juven::config()->db->connectionData;
            $dtp->tableName        = Tools::getPrefixedTableName(juven::config()->db->userExtraData_TablePrefix.$user_id);
            $dtp->sqlQuery         = "SELECT * FROM `".$dtp->tableName."`";
            $dtp->bindParams       = array();
            $dtp->continue         = \TRUE;
            $dtp->userExtraData    = array();
            
            juven::eventsManager()->publish(self::EVENT_onBefore_GetUserExtraDataFromDatabase(), $dtp);
            if ($dtp->continue===\TRUE){
                $select_results = $this->_runSelectQuery($dtp->dbConnectionData, $dtp->sqlQuery, $dtp->bindParams);
                if (\count($select_results)>'0'){ $dtp->userExtraData = $select_results; }
            }
            juven::eventsManager()->publish(self::EVENT_onAfter_GetUserExtraDataFromDatabase(), $dtp);
            return $dtp->userExtraData;
        }

        /**
         * @param string $session_id 
         * @return array Returns Assoc array with session record data
         */
        protected function get_session_record_from_database($session_id){
            $dtp                            = new \stdClass();
            $dtp->dbConnectionData          = juven::config()->db->connectionData;
            $dtp->tableName                 = Tools::getPrefixedTableName(juven::config()->db->tables->sessions);
            $dtp->sqlQuery                  = "SELECT * FROM `".$dtp->tableName."` WHERE `session_id`=:s_id LIMIT 1";
            $dtp->bindParams                = array(':s_id'=> (string)$session_id);
            $dtp->sessionRecordFromDatabase = array();
            $dtp->continue                  = \TRUE;
            
            juven::eventsManager()->publish(self::EVENT_onBefore_GetSessionRecordFromDatabase(), $dtp);
            if ($dtp->continue===\TRUE){
                $select_session_results = $this->_runSelectQuery($dtp->dbConnectionData, $dtp->sqlQuery, $dtp->bindParams);
                if (\count($select_session_results)=='1'){ $dtp->sessionRecordFromDatabase = $select_session_results[0]; unset($select_session_results);}
            }   
            juven::eventsManager()->publish(self::EVENT_onAfter_GetSessionRecordFromDatabase(), $dtp);
            return $dtp->sessionRecordFromDatabase;
        }
        
        /** @return boolean Returns TRUE if user is Guest */
        public function isGuest(){ return $this->_user_data->user_id==juven::config()->db->guest_user_id; }

        private function _runSelectQuery($connectionData, $sql, $bindParams){
            juven::dataBase()->connect($connectionData);
               $re = juven::dataBase()->select($sql, $bindParams);
            juven::dataBase()->disconnect();
            return $re;
        }
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public function __get($name) {
            if (!isset($this->_session->session_data->{$name})){ return \NULL; }
            return $this->_session->session_data->{$name};
        }
        public function __isset($name) { return isset($this->_session->session_data->{$name}); }

        /**
         * @param array $user_data
         */
        public function login(array $user_data){
            // todo: regenerate session_id
            // todo: check if the ip address, browser, etc. of the client are the same before and after login
            $guestUser_data_fromDatabase = $this->getUserDataFromDatabase($user_data["id"]);
            $this->_session->session_data->clear();
            foreach ($guestUser_data_fromDatabase as $key => $value){ $this->_session->session_data->{$key} = $value; }
            $this->_session->session_data->date_last_access = \date ("Y-m-d H:i:s", \time());
            $this->_session->save();
        }

        /**
         * @param string|NULL $redirect_to Optional. Default = NULL
         */
        public function logout($redirect_to=\NULL){
            // todo: regenerate session_id
            $this->_session->session_data->clear();
            $this->_session->session_data->date_last_access = \date ("Y-m-d H:i:s", \time());
            $this->_session->save();
            $this->_session->delete();
            if ($redirect_to==\NULL){ Tools::redirect("index"); } else { Tools::redirect($redirect_to); }
        }

        /**
         * @param string|integer $userID
         * @return array Returns assoc array
         */
        protected function getUser_MenusFromDatabase($userID){
            $dtp = new \stdClass();
            $dtp->table_users_groups     = Tools::getPrefixedTableName(juven::config()->db->table_users_groups);
            $dtp->tableName_menus        = Tools::getPrefixedTableName(juven::config()->db->table_menus);
            $dtp->tableName_menus_groups = Tools::getPrefixedTableName(juven::config()->db->table_menus_groups);
            
// should we get DISTINCT rows ? or NOT ?
//            $sql = "SELECT `tbl_menus`.* FROM `tbl_menus` INNER JOIN (`tbl_menus_groups` "
//                    . " INNER JOIN (SELECT `tbl_users_groups`.`group_id` FROM `tbl_users_groups` WHERE `tbl_users_groups`.`user_id`=:uID) AS `T` "
//                    . "ON `tbl_menus_groups`.`group_id`=`T`.`group_id`) "
//                    . "ON `tbl_menus`.`id`=`tbl_menus_groups`.`menu_id` WHERE `tbl_menus`.`status`=1";            
            $dtp->sqlQuery = "SELECT DISTINCT `".$dtp->tableName_menus."`.* FROM `".$dtp->tableName_menus.
                    "` INNER JOIN (`".$dtp->tableName_menus_groups."` "."INNER JOIN (SELECT `".$dtp->table_users_groups.
                    "`.`group_id` FROM `".$dtp->table_users_groups."` WHERE `".$dtp->table_users_groups."`.`user_id`=:uID) AS `T` ".
                    "ON `".$dtp->tableName_menus_groups."`.`group_id`=`T`.`group_id`) ".
                    "ON `".$dtp->tableName_menus."`.`id`=`".$dtp->tableName_menus_groups."`.`menu_id` WHERE `".
                    $dtp->tableName_menus."`.`status`=1";
            
               
            return $this->turnResources2aTree_v2($this->_runSelectQuery($dtp->sqlQuery, array('uID'=>(string)$userID)));
        }

        
        
        public static function EVENT_onFirstTimeVisitorRequest(){          return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onGuestUserRequest(){                 return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onRegisteredUserRequest(){            return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_GetUserDataFromDatabase(){   return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_getUserDataFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_GetSessionRecordFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_GetSessionRecordFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_GetUserGroupsFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_GetUserGroupsFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_GetUserExtraDataFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_GetUserExtraDataFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onBefore_GetUserHttpRequestsFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        public static function EVENT_onAfter_GetUserHttpRequestsFromDatabase(){    return __CLASS__."\\".__FUNCTION__; }
        
    }
}
?>
